/*
 * Copyright 2011 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package tr.com.netgis;

import tr.com.netgis.util.Constants;
import tr.com.netgis.util.ServiceUtils;
import android.content.ComponentName;
import android.content.Context;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.os.IBinder.DeathRecipient;
import android.os.RemoteException;
import android.util.Log;

/**
 * Wrapper for the connection to the track recording service. This handles
 * connection/disconnection internally, only returning a real service for use if
 * one is available and connected.
 * 
 * @author NetGis
 */
public class MTSServiceConnection {
	
	private IMTSServiceApi boundService;

	private final DeathRecipient deathRecipient = new DeathRecipient() {
		@Override
		public void binderDied() {
			Log.d(Constants.TAG, "Service died");
			setBoundService(null);
		}
	};

	private final ServiceConnection serviceConnection = new ServiceConnection() {
		@Override
		public void onServiceConnected(ComponentName className, IBinder service) {
			Log.i(Constants.TAG, "Connected to service");
			try {
				service.linkToDeath(deathRecipient, 0);
			} catch (RemoteException e) {
				Log.e(Constants.TAG, "Failed to bind a death recipient", e);
			}

			setBoundService(IMTSServiceApi.Stub.asInterface(service));
		}

		@Override
		public void onServiceDisconnected(ComponentName className) {
			Log.i(Constants.TAG, "Disconnected from service");
			setBoundService(null);
		}
	};

	private final Context context;

	private final Runnable bindChangedCallback;

	/**
	 * Constructor.
	 * 
	 * @param context
	 *            the current context
	 * @param bindChangedCallback
	 *            a callback to be executed when the state of the service
	 *            binding changes
	 */
	public MTSServiceConnection(Context context, Runnable bindChangedCallback) {
		this.context = context;
		this.bindChangedCallback = bindChangedCallback;
	}

	/**
	 * Binds to the service, starting it if necessary.
	 */
	public void startAndBind() {
		bindService(true);
	}

	/**
	 * Binds to the service, only if it's already running.
	 */
	public void bindIfRunning() {
		bindService(false);
	}

	/**
	 * Unbinds from and stops the service.
	 */
	public void stop() {
		unbind();

		Log.d(Constants.TAG, "Stopping service");
		context.stopService(Constants.MTS_SERVICE_INTENT);
	}

	/**
	 * Unbinds from the service (but leaves it running).
	 */
	public void unbind() {
		Log.d(Constants.TAG, "Unbinding from the service");
		try {
			context.unbindService(serviceConnection);
		} catch (IllegalArgumentException e) {
			// Means we weren't bound, which is ok.
		}

		setBoundService(null);
	}

	/**
	 * Returns the service if connected to it, or null if not connected.
	 */
	public IMTSServiceApi getServiceIfBound() {
		checkBindingAlive();

		return boundService;
	}

	private void checkBindingAlive() {
		if (boundService != null && !boundService.asBinder().isBinderAlive()) {
			setBoundService(null);
		}
	}

	private void bindService(boolean startIfNeeded) {
		if (boundService != null) {
			// Already bound.
			return;
		}

		if (!startIfNeeded && !ServiceUtils.isServiceRunning(context)) {
			// Not running, start not requested.
			Log.d(Constants.TAG, "Service not running, not binding to it.");
			return;
		}

		if (startIfNeeded) {
			Log.i(Constants.TAG, "Starting the service");
			context.startService(Constants.MTS_SERVICE_INTENT);
		}

		Log.i(Constants.TAG, "Binding to the service");
		context.bindService(Constants.MTS_SERVICE_INTENT, serviceConnection, 0);
	}

	private void setBoundService(IMTSServiceApi service) {
		boundService = service;
		if (bindChangedCallback != null) {
			bindChangedCallback.run();
		}
	}
}
